import { host } from '../env'
import getErrorMessage from './expectionalError'
import {
  getAccessToken,
  getStorage,
  setStorage,
  clearToken,
  saveTokens,
} from './storageSyncTool'
// import { getCookie } from './wxTool';
class wxRequest {
  constructor() {
    this.invalidCodes = [401, 10000, 422] // token 失效
    this.hideMsgCodes = [401, 404, 10000, 10030] // 隐藏提示
    this.qeueu = [] // 因 token失效后 存储的请求队列
    this.isTokening = false // token是否请求中
  }
  // 统一请求
  request({
    url,
    params,
    data = {},
    method = 'GET',
    isLogin = false,
    isNeedUserInfo,
  }) {
    if (!url.startsWith('http')) url = host + url
    let accessToken = getAccessToken()
    // need_update => 是否需要上传用户信息
    let { need_update } = getStorage('isUpdata')
    // 用户信息未上传 开启全局弹窗
    if (isNeedUserInfo && need_update)
      return (getApp().globalData.needUpdate = need_update)

    // 格式化请求参数
    let options = Object.assign(
      {},
      {
        url,
        data: params || data,
        method,
        header: isLogin
          ? {}
          : {
              'Content-Type': 'application/json',
              Authorization: accessToken,
            },
      },
      ...[].slice.call(arguments, 1)
    )
    return new Promise((resolve, reject) => {
      // 首次登陆（空token时） 从新登陆
      if (!isLogin && !accessToken) return this._login(options, resolve, reject)
      wx.request({
        ...options,
        // statusCode 》为网络状态码
        success: ({ header, data, statusCode, errMsg }) => {
          // 存储cookie
          // let cookie = getCookie(header['Set-Cookie'])
          // setStorage('cookie', cookie)
          // 判断 网络状态码是否为 2开头
          let isStatusOk = ''.startsWith.call(statusCode, 2)
          // 检测 返回数据是否正常
          let isDataOk =
            Object.prototype.toString.call(data).slice(8, -1) === 'Object'
          // 返回数据正常 始终以业务错误码为准
          if (isDataOk)
            this._formatResponseData(data, statusCode, {
              errCodeKey: 'code',
              errMsgKey: 'msg',
            })
          // 返回数据异常 以请求的基本错误码为准
          else if (!isStatusOk) data = { statusCode, errMsg }
          // token失效
          if (!isLogin && this._isTokenInvalid(data)) {
            return this._login(options, resolve, reject)
          }
          // isStatusOk -- 网络码正常，data.errCode -- 业务码正常
          isStatusOk && !data.errCode ? resolve(data) : reject(data)
        },
        fail: (err) => reject(err),
      })
    }).catch((err) => {
      if (!this._isShowMsg(err)) {
        wx.showToast({
          title: getErrorMessage(err).description,
          // title: err.errMsg,
          icon: 'none',
          duration: 2000,
        })
      }
      return Promise.reject(err)
    })
  }

  // 从新登陆
  _login(options, resolve, reject) {
    // 将因token失效的请求存入 队列
    this._pushQeueu(options, resolve, reject)
    // 若当前token处于请求中，则中断，防止重复刷新token
    if (this.isTokening) return false
    return this._getToken(options)
      .then(({ data }) => {
        let { access_token, need_update, need_phone, refresh_token } = data
        saveTokens(access_token, refresh_token)
        setStorage('isUpdata', { need_update, need_phone })
        this.isTokening = false
        // 是否需上传用户信息
        if (need_update) return this._getNeedUpdata(options)
        // 从新发送因token失效的请求
        this._execQeueu() // token 更新成功，执行请求队列
        // return Promise.resolve(this.request(options))
      })
      .catch(() => {})
  }

  // 更新用户信息
  _getNeedUpdata(options) {
    return wx
      .getSetting()
      .then(({ authSetting }) => {
        // 用户已授权 静默上传用户信息
        if (authSetting['scope.userInfo']) return wx.getUserInfo()
        // 未授权则开启自定义授权弹窗
        else getApp().globalData.needUpdate = true
      })
      .then((res) => res && getApp()._getUserInfo(res))
      .then(() => this.request(options))
  }

  // 获取token
  _getToken() {
    clearToken()
    // 标记当前token处于请求中
    this.isTokening = true
    return new Promise((success, fail) => wx.login({ success, fail })).then(
      ({ code }) =>
        this.request({
          url: `/api/v1/user/get_token`,
          data: { code },
          method: 'POST',
          isLogin: true,
        })
    )
  }

  // 统一 返回的业务数据 key值
  _formatResponseData(data, statusCode, { errCodeKey, errMsgKey }) {
    if (errCodeKey && !data.hasOwnProperty('errCode'))
      // 统一错误码字段名为：errCode
      data['errCode'] = data[errCodeKey]
    if (!data.errCode) data['statusCode'] = statusCode
    if (errMsgKey && !data.hasOwnProperty('errMsg'))
      // 统一错误提示信息为：errMsg
      data['errMsg'] = data[errMsgKey]
    ;[errCodeKey, errMsgKey].map((v) => delete data[v]) // 移除旧属性
  }

  // 存储请求队列
  _pushQeueu(options, resolve, reject) {
    this.qeueu.push({ options, resolve, reject })
  }

  // 执行请求队列
  _execQeueu() {
    let promises = this.qeueu.map((v) =>
      this.request(v.options).then(v.resolve).catch(v.reject)
    )
    Promise.all(promises).finally(() => {
      // 清空任务队列
      this.qeueu.length = 0
    })
  }
  // 是否为token失效
  _isTokenInvalid({ errCode, statusCode }) {
    return [errCode, statusCode].some((code) =>
      this.invalidCodes.includes(code)
    )
  }
  // 是否展示错误提示
  _isShowMsg({ error_code, statusCode }) {
    return [error_code, statusCode].some((code) =>
      this.hideMsgCodes.includes(code)
    )
  }
}

export default wxRequest
